/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.inspur.edp.lcm.metadata.core;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.lcm.metadata.api.entity.GspProject;
import com.inspur.edp.lcm.metadata.api.entity.MetadataPackage;
import com.inspur.edp.lcm.metadata.api.entity.MetadataPackageHeader;
import com.inspur.edp.lcm.metadata.api.entity.MetadataPackageReference;
import com.inspur.edp.lcm.metadata.api.entity.MetadataPackageVersion;
import com.inspur.edp.lcm.metadata.api.entity.MetadataProject;
import com.inspur.edp.lcm.metadata.api.entity.ServiceUnitInfo;
import com.inspur.edp.lcm.metadata.api.service.FileService;
import com.inspur.edp.lcm.metadata.common.FileServiceImp;
import com.inspur.edp.lcm.metadata.common.Utils;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import static com.inspur.edp.lcm.metadata.common.Utils.handlePath;

/**
 * @author zhaoleitr
 */
public class PackageGenerateCoreService {
    private final MetadataProjectCoreService projectService = new MetadataProjectCoreService();

    private String packageName = "";
    private String projectPath = "";
    private String packagePath = "";
    private String projectFileName = "";
    private MetadataPackageHeader metadataPackageHeader;
    private List<GspMetadata> metadataList;
    private Map<String, String> metadataAndDirPathList;
    private MetadataPackageVersion packageVersion = new MetadataPackageVersion();
    private final FileServiceImp fileService = new FileServiceImp();

    public void generatePackage(String path) {
        projectPath = handlePath(path);
        //1、初始化参数
        initialize(projectPath);
        //2、组织manifest文件
        getManifest();
        //3、打包
        publishPackage();
    }

    private void initialize(String path) {
        MetadataProject project = projectService.getMetadataProjInfo(path);
        projectFileName = project.getName() + Utils.getMetadataProjSuffix();
        packageName = project.getMetadataPackageInfo().getName();
        packageVersion = project.getMetadataPackageInfo().getVersion();
        metadataPackageHeader = project.getMetadataPackageInfo();

        String tempPath = fileService.getCombinePath(projectPath, "bin");
        packagePath = fileService.getCombinePath(tempPath, UUID.randomUUID().toString());
        metadataAndDirPathList = new LinkedHashMap<>();
        metadataList = new ArrayList<>();
    }

    private void getManifest() {
        //1、获取头结点
        MetadataPackageHeader mpHeader = metadataPackageHeader;
        //2、获取SU信息
        ServiceUnitInfo suInfo = getServiceUnitInfo();
        //3、获取依赖关系
        List<MetadataPackageReference> mpRefs = getMpReference();
        //4、查找元数据前处理
        handleMetadataBeforePackage(metadataAndDirPathList, projectPath, packagePath);
        //5、查找元数据后处理
        handleMetadataAfterFinding(mpHeader, suInfo, mpRefs);
    }

    private void publishPackage() {
        Utils.compress(packagePath, fileService.getDirectoryName(packagePath), this.packageName);
        //将新建的文件夹删除，只保留生成的元数据包文件
        handleMetadataAfterPackage(packagePath);
    }

    private MetadataPackageHeader getMpHeader() {
        MetadataPackageHeader mpHeader = new MetadataPackageHeader();
        mpHeader.setName(packageName);
        mpHeader.setVersion(packageVersion);

        return mpHeader;
    }

    private ServiceUnitInfo getServiceUnitInfo() {
        // 获取工程信息
        GspProject gspProject = new GspProjectCoreService().getGspProjectInfo(projectPath);
        // 从工程信息中获取服务单元信息
        ServiceUnitInfo suInfo = new ServiceUnitInfo();
        suInfo.setAppCode(gspProject.getAppCode());
        suInfo.setServiceUnitCode(gspProject.getServiceUnitCode());

        return suInfo;
    }

    private List<MetadataPackageReference> getMpReference() {
        String projectFilePath = fileService.getCombinePath(projectPath, projectFileName);
        if (!fileService.isFileExist(projectFilePath)) {
            return null;
        }
        List<MetadataPackageReference> mpReferences = new ArrayList<>();
        FileService service = new FileServiceImp();
        String projStr = null;
        try {
            projStr = service.fileRead(projectFilePath);
        } catch (IOException e) {
            e.printStackTrace();
        }
        ObjectMapper objectMapper = Utils.getMapper();
        MetadataProject project;
        try {
            project = objectMapper.readValue(projStr, MetadataProject.class);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        if (project.getMetadataPackageRefs() == null) {
            return null;
        }
        for (MetadataPackageHeader item : project.getMetadataPackageRefs()) {
            MetadataPackageReference mpRefs = new MetadataPackageReference();
            mpRefs.setDepententPackage(new MetadataPackageHeader());
            mpRefs.getDepententPackage().setName(item.getName());
            if (item.getVersion().getVersionString().contains("-")) {
                mpRefs.getDepententPackage().setVersion(new MetadataPackageVersion(item.getVersion().getVersionString().substring(0, item.getVersion().getVersionString().indexOf("-"))));
            } else {
                mpRefs.getDepententPackage().setVersion(new MetadataPackageVersion(item.getVersion()));
            }
            mpReferences.add(mpRefs);
        }

        return mpReferences;
    }

    private void handleMetadataBeforePackage(Map<String, String> hashMap, String projectPath, String packagePath) {
        //找到工程下所有的元数据及文件夹列表
        findMdAndDirPathWithinProject(hashMap, projectPath);
        //创建临时文件夹
        if (!fileService.isDirectoryExist(packagePath)) {
            fileService.createDirectory(packagePath);
        }
        //拷贝元数据文件到临时路径下
        copyMdFiles(hashMap, projectPath, packagePath);
        if (hashMap != null && hashMap.size() > 0) {
            for (String item : hashMap.keySet()) {
                String relativePath;
                if ("File".equals(hashMap.get(item))) {
                    GspMetadata metadata = new MetadataCoreManager().getMetadataWithoutContent(item);
                    relativePath = item.replace(projectPath, "");
                    metadata.setRelativePath(Utils.handlePath(relativePath));
                    if (!metadataList.contains(metadata)) {
                        metadataList.add(metadata);
                    }
                }
            }
        }
    }

    private void handleMetadataAfterFinding(MetadataPackageHeader mpHeader, ServiceUnitInfo suInfo,
        List<MetadataPackageReference> mpReference) {
        MetadataPackage metadataPackage = new MetadataPackage();
        metadataPackage.setHeader(mpHeader);
        metadataPackage.setServiceUnitInfo(suInfo);
        metadataPackage.setReference(mpReference);
        metadataPackage.setMetadataList(new ArrayList<>());
        if (metadataList != null && metadataList.size() > 0) {
            metadataList.forEach(item -> metadataPackage.getMetadataList().add(item));
        }
        ObjectMapper objectMapper = new ObjectMapper();
        String metadataPackageStr;
        try {
            metadataPackageStr = objectMapper.writeValueAsString(metadataPackage);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        String manifestPath = fileService.getCombinePath(packagePath, Utils.getManifestFileName());
        if (!fileService.isFileExist(manifestPath)) {
            try {
                fileService.createFile(packagePath, Utils.getManifestFileName());
                fileService.fileUpdate(manifestPath, metadataPackageStr);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        } else {
            fileService.fileUpdate(manifestPath, metadataPackageStr);
        }
    }

    private void findMdAndDirPathWithinProject(Map<String, String> hashMap, String projectPath) {

        List<File> dirs = fileService.getDirectorys(projectPath);
        if (dirs.size() > 0) {
            for (File dir : dirs) {
                String dirName = fileService.getFileName(handlePath(dir.getPath()));
                String temPath = projectPath + File.separator + dirName;
                if (!hashMap.containsKey(temPath) && !".git".equals(dirName) && !".vs".equals(dirName) && !"bin".equals(dirName) && !"publish".equals(dirName) && !"obj".equals(dirName) && !"src".equals(dirName)) {
                    hashMap.put(handlePath(temPath), "Directory");
                    findMdAndDirPathWithinProject(hashMap, temPath);
                }
            }
        }

        List<File> paths = fileService.getAllFiles(projectPath);
        if (paths.size() > 0) {
            boolean isMetadataExisting = false;
            for (File path : paths) {
                String extension = fileService.getExtension(handlePath(path.getPath())).toLowerCase();
                //TODO 这里需要从配置文件读取元数据后缀类型
                List<String> metadataAllTypes = Utils.getMetadataPostfixTypes();
                boolean flag = false;
                for (String item : metadataAllTypes) {
                    if (item.toLowerCase().equals(extension)) {
                        flag = true;
                        break;
                    }
                }
                if (flag) {
                    hashMap.put(handlePath(path.getPath()), "File");
                    isMetadataExisting = true;
                }
            }
            if (!isMetadataExisting) {
                hashMap.remove(handlePath(projectPath));
            }
        } else {
            hashMap.remove(handlePath(projectPath));
        }
    }

    private void copyMdFiles(Map<String, String> hashMap, String projectPath, String packagePath) {
        //将工程下的元数据文件都拷贝到新建的以程序集名称的文件夹下
        String absolutePath;
        String relativePath;
        if (hashMap == null || metadataAndDirPathList.size() == 0) {
            return;
        }
        for (String path : metadataAndDirPathList.keySet()) {
            relativePath = path.replace(projectPath, "").substring(1);
            absolutePath = fileService.getCombinePath(packagePath, relativePath);
            if ("Directory".equals(hashMap.get(path))) {
                if (!fileService.isDirectoryExist(absolutePath)) {
                    fileService.createDirectory(absolutePath);
                }
            } else {
                if (fileService.isFileExist(absolutePath)) {
                    try {
                        fileService.fileDelete(absolutePath);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
                copyMdFiles(path, absolutePath);
            }
        }
    }

    private void copyMdFiles(String sourcePath, String destinationPath) {
        try {
            fileService.fileCopy(sourcePath, destinationPath);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void handleMetadataAfterPackage(String path) {
        try {
            fileService.deleteAllFilesUnderDirectory(path);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public List<MetadataPackage> getLocalPackages(String absolutePath) {
        List<MetadataPackage> metadataPackages = new ArrayList<>();
        File[] files = new File(absolutePath).listFiles(pathname -> pathname.getName().endsWith(Utils.getMetadataProjSuffix()));
        if (files != null && files.length > 0) {
            MetadataCoreManager metadataCoreManager = new MetadataCoreManager();
            for (File file : files) {
                metadataPackages.add(metadataCoreManager.getMetadataPackageInfo(file.getName(), file.getParent()));
            }
        }
        return metadataPackages;
    }
}
